package com.pai4j.pgc.service.resource;
import com.pai4j.common.bean.PAIPageResponseBeanUtil;
import com.pai4j.common.enums.community.ResourcePathEnum;
import com.pai4j.common.util.DateUtils;
import com.pai4j.common.util.JsonUtil;
import com.pai4j.common.util.RedisUtil;
import com.pai4j.common.util.community.BatchHandleExecutor;
import com.pai4j.domain.community.vo.response.resource.HomeResourceBaseResponseVO;
import com.pai4j.domain.community.vo.response.resource.ResourceBaseResponseVO;
import com.pai4j.domain.community.vo.response.resource.ResourceCountResponseVO;
import com.pai4j.domain.community.vo.response.resource.ResourceDetailResponseVO;
import com.pai4j.pgc.dao.community.IResourceDAO;
import com.pai4j.pgc.dao.community.IResourceDownloadDAO;
import com.pai4j.pgc.entity.community.ResourceDownloadEntity;
import com.pai4j.pgc.entity.community.ResourceEntity;
import com.pai4j.pgc.pojo.convert.ResourceEntityConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import com.pai4j.common.enums.RedisKeyEnum;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 *  资源列表服务、排序
 *
 * @author: CYM-pai
 * @date: 2025/10/27 17:37
 **/
@Slf4j
@Service
public class ResourceListService {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private IResourceDAO resourceDAO;
    @Autowired
    private ResourceCacheService resourceCacheService;
    @Autowired
    private IResourceDownloadDAO resourceDownloadDAO;

    @Autowired
    @Qualifier("bulkExecutor")
    private Executor bulkExecutor;

    /***
     * 最新榜限制最新500个资源
     */
    private static final int REAL_TIME_MAX_SIZE = 500;

    /**
     * CTC 首页精选资源条数
     */
    private static final int CTC_HOME_RESOURCE_SIZE = 6;
    /***
     * 精品资源榜
     */
    private static final int BOUTIQUE_MAX_SIZE = 100;

    /***
     * 首页资源查询
     * @param size
     * @return
     */
    public HomeResourceBaseResponseVO homeResources(int size) {
        HomeResourceBaseResponseVO homeResourceBaseResponseVO = new HomeResourceBaseResponseVO();
        BatchHandleExecutor batchHandleExecutor = BatchHandleExecutor.init(3, bulkExecutor);
        batchHandleExecutor.execute(() -> homeResourceBaseResponseVO.setHotResources(
                this.getResourceHotDownloadListFromCache(1, size, true, true)
        ));
        batchHandleExecutor.execute(() -> homeResourceBaseResponseVO.setVipResources(
                this.getResourceBoutiqueListFromCache(1, size, true, true)
        ));
        batchHandleExecutor.execute(() -> homeResourceBaseResponseVO.setBookResources(
                this.getResourceSyncHomeListFromCache(ResourcePathEnum.PDF.name(),1, size, true, true)
        ));
        batchHandleExecutor.await();
        return homeResourceBaseResponseVO;
    }

    /***
     * 查询瑞索斯首页数据
     * @param path
     * @param pageNo
     * @param pageSize
     * @param needAuthor
     * @param queryCountInfo
     * @return
     */
    public List<ResourceBaseResponseVO> getResourceSyncHomeListFromCache(String path, int pageNo, int pageSize,
                                                                         boolean needAuthor, boolean queryCountInfo) {
        String key = RedisKeyEnum.RESOURCE_SYNC_HOME_LIST_CACHE.getKey(path);
        String val = redisUtil.get(key);
        List<String> resourceIdList;
        if (StringUtils.isNotBlank(val)) {
            resourceIdList = JsonUtil.fromJson(val, List.class);
        } else {
            resourceIdList = refreshSyncHomeListCache(path);
        }
        /***
         * 内存中完成分页查询
         */
        return this.buildQueryResult(resourceIdList, pageNo, pageSize, needAuthor, queryCountInfo);
    }

    /***
     * 查询瑞索斯实时最新榜单数据
     * @param path
     * @param pageNo
     * @param pageSize
     * @param needAuthor
     * @param queryCountInfo
     * @return
     */
    public List<ResourceBaseResponseVO> getResourceRealTimeListFromCache(String path, int pageNo, int pageSize,
                                                                         boolean needAuthor, boolean queryCountInfo) {
        String key = RedisKeyEnum.RESOURCE_REAL_TIME_LIST_CACHE.getKey(path == null ? "" : path);
        String val = redisUtil.get(key);
        List<String> resourceIdList;
        if (StringUtils.isNotBlank(val)) {
            resourceIdList = JsonUtil.fromJson(val, List.class);
        } else {
            resourceIdList = refreshRealTimeNewListByPath(path);
        }
        /***
         * 内存中完成分页查询
         */
        return this.buildQueryResult(resourceIdList, pageNo, pageSize, needAuthor, queryCountInfo);
    }

    public List<ResourceBaseResponseVO> getCTCHomeResource(Long ccid, int size) {
        String key = RedisKeyEnum.CTC_RESOURCE_SYNC_HOME_LIST_CACHE.getKey(ccid.toString());
        String val = redisUtil.get(key);
        List<String> resourceIdList;
        if (StringUtils.isNotBlank(val)) {
            resourceIdList = JsonUtil.fromJson(val, List.class);
        } else {
            resourceIdList = refreshSyncCTCHomeListCache(ccid);
        }
        /***
         * 内存中完成分页查询
         */
        return this.buildQueryResult(resourceIdList, 1, size, false, false);
    }

    /***
     * 查询瑞索斯精品资源榜单数据
     * @param pageNo
     * @param pageSize
     * @param needAuthor
     * @param queryCountInfo
     * @return
     */
    public List<ResourceBaseResponseVO> getResourceBoutiqueListFromCache(int pageNo, int pageSize,
                                                                         boolean needAuthor, boolean queryCountInfo) {
        String key = RedisKeyEnum.RESOURCE_BOUTIQUE_LIST_CACHE.getKey();
        String val = redisUtil.get(key);
        List<String> resourceIdList;
        if (StringUtils.isNotBlank(val)) {
            resourceIdList = JsonUtil.fromJson(val, List.class);
        } else {
            resourceIdList = refreshBoutiqueListCache();
        }
        /***
         * 内存中完成分页查询
         */
        List<ResourceBaseResponseVO> resourceBaseResponseVOS = this.buildQueryResult(resourceIdList, pageNo, pageSize, needAuthor, queryCountInfo);
        // 随机结果
        return new ArrayList<>(new HashSet<>(resourceBaseResponseVOS));
    }

    /***
     * 查询瑞索斯精品资源榜单数据
     * @param pageNo
     * @param pageSize
     * @param needAuthor
     * @param queryCountInfo
     * @return
     */
    public List<ResourceBaseResponseVO> getResourceHotDownloadListFromCache(int pageNo, int pageSize,
                                                                            boolean needAuthor, boolean queryCountInfo) {
        String key = RedisKeyEnum.RESOURCE_HOT_DOWNLOAD_LIST_CACHE.getKey();
        String val = redisUtil.get(key);
        List<String> resourceIdList;
        if (StringUtils.isNotBlank(val)) {
            resourceIdList = JsonUtil.fromJson(val, List.class);
        } else {
            resourceIdList = refreshHotDownloadListCache();
        }
        /***
         * 内存中完成分页查询
         */
        List<ResourceBaseResponseVO> list = this.buildQueryResult(resourceIdList, pageNo, pageSize, needAuthor, queryCountInfo);
        if (CollectionUtils.isEmpty(list)) {
            // 前期没有资源，降级策略
            list = getResourceRealTimeListFromCache(null, pageNo, pageSize, needAuthor, queryCountInfo);
        }
        return list;
    }


    /***
     * 刷新瑞索斯资源实时最新榜单
     */
    public void refreshAllRealTimeNewList() {
        // 刷新全部类型实时最新热榜
        refreshRealTimeNewListByPath(null);
        // 刷新分类下的资源实时最新榜单
        ResourcePathEnum.getAllResourcePath().forEach(path -> refreshRealTimeNewListByPath(path));
    }

    /***
     * 刷新瑞索斯资源同步首页的榜单
     */
    public void refreshAllSyncHomeList() {
        ResourcePathEnum.getAllResourcePath().forEach(path -> refreshSyncHomeListCache(path));
    }

    /***
     * 刷新指定栏目实时最新榜单
     * @param path
     */
    public void refreshRealTimeNewList(String path) {
        // 刷新全部类型实时最新热榜
        refreshRealTimeNewListByPath(null);
        // 刷新分类下的资源实时最新榜单
        refreshRealTimeNewListByPath(path);
    }

    public void refreshCtcRealTimeNewList(Long ccid, String resourceId, long id) {
        String key = RedisKeyEnum.CTC_RESOURCE_REAL_TIME_LIST_CACHE.getKey(ccid.toString());
        if (redisUtil.exists(key)) {
            redisUtil.zadd(key, resourceId, id, RedisKeyEnum.CTC_RESOURCE_REAL_TIME_LIST_CACHE.getExpireTime());
            return;
        }
        List<ResourceEntity> realTimeResourceList = resourceDAO.findCTCRealTimeResourceIdListByType(ccid, REAL_TIME_MAX_SIZE);
        if (CollectionUtils.isEmpty(realTimeResourceList)) {
            log.warn("refresh ctc real time resource list cache warn. query db result is empty! ccid:{}", ccid);
            return;
        }
        Set<ZSetOperations.TypedTuple<String>> tuples = realTimeResourceList.stream().map(
                res -> {
                    String value = res.getResourceId();
                    Double score = Double.valueOf(res.getCreateDate().getTime());
                    return new DefaultTypedTuple<>(value, score);
                }).collect(Collectors.toSet());
        redisUtil.zadd(key, tuples, RedisKeyEnum.CTC_RESOURCE_REAL_TIME_LIST_CACHE.getExpireTime());

    }

    public PAIPageResponseBeanUtil<List<ResourceBaseResponseVO>> pageCtcRealTimeNewListFromCache(Long ccid, int pageNo, int pageSize) {
        String key = RedisKeyEnum.CTC_RESOURCE_REAL_TIME_LIST_CACHE.getKey(ccid.toString());
        if (!redisUtil.exists(key)) {
            this.refreshCtcRealTimeResource(ccid);
        }
        Set<String> resourceIdList = redisUtil.zreverseRange(key, (pageNo - 1) * pageSize, pageNo * pageSize);
        List<ResourceBaseResponseVO> list = buildQueryResult(new ArrayList<>(resourceIdList), true, true);
        Long total = redisUtil.zsize(key);
        return PAIPageResponseBeanUtil.success(pageNo, pageSize, total, list);
    }

    private void refreshCtcRealTimeResource(Long ccid) {
        resourceCacheService.initCTCNewResourceCache(ccid);
    }

    /***
     * 刷新分类下实时热榜
     * @param path
     */
    public List<String> refreshRealTimeNewListByPath(String path) {
        List<String> realTimeResourceIdList;
        if (StringUtils.isBlank(path)) {
            realTimeResourceIdList = resourceDAO.findRealTimeResourceIdList(REAL_TIME_MAX_SIZE);
        } else {
            realTimeResourceIdList = resourceDAO.findRealTimeResourceIdListByType(path, REAL_TIME_MAX_SIZE);
        }
        if (CollectionUtils.isEmpty(realTimeResourceIdList)) {
            log.warn("refresh real time resource list cache warn. query db result is empty! path:{}", path);
            return null;
        }
        String key = RedisKeyEnum.RESOURCE_REAL_TIME_LIST_CACHE.getKey(path == null ? "" : path);
        redisUtil.set(key, JsonUtil.toJsonString(realTimeResourceIdList), RedisKeyEnum.RESOURCE_REAL_TIME_LIST_CACHE.getExpireTime());
        log.info("refresh real time resource list cache success. path:{}", path);
        return realTimeResourceIdList;
    }

    /***
     * 刷新分类下实时热榜
     * @param path
     */
    public List<String> refreshSyncHomeListCache(String path) {
        List<String> syncHomeResourceIdList = resourceDAO.findHomeResourceIdListByType(path, REAL_TIME_MAX_SIZE);
        if (CollectionUtils.isEmpty(syncHomeResourceIdList)) {
            log.warn("refresh sync home resource list cache warn. query db result is empty! path:{}", path);
            return null;
        }
        String key = RedisKeyEnum.RESOURCE_SYNC_HOME_LIST_CACHE.getKey(path == null ? "" : path);
        redisUtil.set(key, JsonUtil.toJsonString(syncHomeResourceIdList), RedisKeyEnum.RESOURCE_SYNC_HOME_LIST_CACHE.getExpireTime());
        log.info("refresh sync home resource list cache success. path:{}", path);
        return syncHomeResourceIdList;
    }

    /**
     * 刷新同步CTC首页大的资源合集
     * @param ccid
     * @return
     */
    public List<String> refreshSyncCTCHomeListCache(Long ccid) {
        List<String> syncHomeResourceIdList = resourceDAO.findCTCHomeResourceIdListByType(ccid, CTC_HOME_RESOURCE_SIZE);
        if (CollectionUtils.isEmpty(syncHomeResourceIdList)) {
            log.warn("refresh sync ctc home resource list cache warn. query db result is empty! ccid:{}", ccid);
            return null;
        }
        String key = RedisKeyEnum.CTC_RESOURCE_SYNC_HOME_LIST_CACHE.getKey(ccid.toString());
        redisUtil.set(key, JsonUtil.toJsonString(syncHomeResourceIdList), RedisKeyEnum.CTC_RESOURCE_SYNC_HOME_LIST_CACHE.getExpireTime());
        log.info("refresh sync ctc  home resource list cache success. ccid:{}", ccid);
        return syncHomeResourceIdList;
    }

    /***
     * 刷新精品资源榜单
     */
    public List<String> refreshBoutiqueListCache() {
        List<String> boutiqueResourceIdList = resourceDAO.findBoutiqueResourceIdList(BOUTIQUE_MAX_SIZE);
        if (CollectionUtils.isEmpty(boutiqueResourceIdList)) {
            log.warn("refresh boutique resource list cache warn. query db result is empty!");
            return null;
        }
        String key = RedisKeyEnum.RESOURCE_BOUTIQUE_LIST_CACHE.getKey();
        redisUtil.set(key, JsonUtil.toJsonString(boutiqueResourceIdList), RedisKeyEnum.RESOURCE_BOUTIQUE_LIST_CACHE.getExpireTime());
        log.info("refresh boutique resource list cache success.");
        return boutiqueResourceIdList;
    }

    /***
     * 刷新热门下载资源榜单
     */
    public List<String> refreshHotDownloadListCache() {
        List<ResourceDownloadEntity> resourceDownloadEntities = resourceDownloadDAO.findAllByDownloadDateAfter(DateUtils.getBeforeNDayDate(90, new Date()));
        if (CollectionUtils.isEmpty(resourceDownloadEntities)) {
            log.warn("refresh hot download resource list cache warn. query db result is empty!");
            return null;
        }
        List<String> hotDownloadResourceIdList = resourceDownloadEntities.stream().map(ResourceDownloadEntity::getResourceId).distinct().collect(Collectors.toList());
        String key = RedisKeyEnum.RESOURCE_HOT_DOWNLOAD_LIST_CACHE.getKey();
        redisUtil.set(key, JsonUtil.toJsonString(hotDownloadResourceIdList), RedisKeyEnum.RESOURCE_HOT_DOWNLOAD_LIST_CACHE.getExpireTime());
        log.info("refresh hot download resource list cache success.");
        return hotDownloadResourceIdList;
    }

    /***
     * 构造榜单查询结果
     * @param resourceIdList
     * @param pageNo
     * @param pageSize
     * @param needAuthor
     * @param queryCountInfo
     * @return
     */
    private List<ResourceBaseResponseVO> buildQueryResult(List<String> resourceIdList, int pageNo, int pageSize,
                                                          boolean needAuthor, boolean queryCountInfo) {
        List<ResourceBaseResponseVO> queryResultList = new ArrayList<>();
        if (CollectionUtils.isEmpty(resourceIdList)) {
            return queryResultList;
        }
        /***
         * 内存中完成分页查询
         */
        int len = resourceIdList.size();
        int start = (pageNo - 1) * pageSize;
        if (start >= len) {
            return queryResultList;
        }
        int end = start + pageSize;
        end = len > end ? end : resourceIdList.size();
        resourceIdList = resourceIdList.subList(start, end);
        // 填充资源详情数据
        Map<String, ResourceDetailResponseVO> resourceDetailResponseVOMap =
                resourceCacheService.batchGetResourceDetailFromCache(resourceIdList, needAuthor, queryCountInfo);

        Map<String, Long> viewCountMap = new HashMap<>();
        Map<String, Long> downloadCountMap = new HashMap<>();
        if (queryCountInfo) {
           viewCountMap = resourceCacheService.batchGetResourceViewCountFromCache(resourceIdList);
           downloadCountMap = resourceCacheService.batchGetResourceDownloadCountFromCache(resourceIdList);
        }
        Map<String, Long> finalViewCountMap = viewCountMap;
        Map<String, Long> finalDownloadCountMap = downloadCountMap;
        resourceIdList.stream().forEach(resId -> {
            ResourceDetailResponseVO resourceDetailResponseVO;
            if ((resourceDetailResponseVO = resourceDetailResponseVOMap.get(resId)) != null) {
                ResourceBaseResponseVO resourceBaseResponseVO = ResourceEntityConvert.detail2Base(resourceDetailResponseVO);
                if (resourceBaseResponseVO.getResourceCount() != null) {
                    resourceBaseResponseVO.getResourceCount().setViewCount(finalViewCountMap.getOrDefault(resId, 0L));
                    resourceBaseResponseVO.getResourceCount().setDownloadCount(finalDownloadCountMap.getOrDefault(resId, 0L));
                } else {
                    ResourceCountResponseVO resourceCount = new ResourceCountResponseVO();
                    resourceCount.setViewCount(finalViewCountMap.getOrDefault(resId, 0L));
                    resourceCount.setDownloadCount(finalDownloadCountMap.getOrDefault(resId, 0L));
                    resourceBaseResponseVO.setResourceCount(resourceCount);
                }
                queryResultList.add(resourceBaseResponseVO);
            }
        });
        return queryResultList;
    }

    private List<ResourceBaseResponseVO> buildQueryResult(List<String> resourceIdList, boolean needAuthor, boolean queryCountInfo) {
        List<ResourceBaseResponseVO> queryResultList = new ArrayList<>();
        if (CollectionUtils.isEmpty(resourceIdList)) {
            return queryResultList;
        }
        // 填充资源详情数据
        Map<String, ResourceDetailResponseVO> resourceDetailResponseVOMap =
                resourceCacheService.batchGetResourceDetailFromCache(resourceIdList, needAuthor, queryCountInfo);

        Map<String, Long> viewCountMap = new HashMap<>();
        Map<String, Long> downloadCountMap = new HashMap<>();
        if (queryCountInfo) {
            viewCountMap = resourceCacheService.batchGetResourceViewCountFromCache(resourceIdList);
            downloadCountMap = resourceCacheService.batchGetResourceDownloadCountFromCache(resourceIdList);
        }
        Map<String, Long> finalViewCountMap = viewCountMap;
        Map<String, Long> finalDownloadCountMap = downloadCountMap;
        resourceIdList.stream().forEach(resId -> {
            ResourceDetailResponseVO resourceDetailResponseVO;
            if ((resourceDetailResponseVO = resourceDetailResponseVOMap.get(resId)) != null) {
                ResourceBaseResponseVO resourceBaseResponseVO = ResourceEntityConvert.detail2Base(resourceDetailResponseVO);
                if (resourceBaseResponseVO.getResourceCount() != null) {
                    resourceBaseResponseVO.getResourceCount().setViewCount(finalViewCountMap.getOrDefault(resId, 0L));
                    resourceBaseResponseVO.getResourceCount().setDownloadCount(finalDownloadCountMap.getOrDefault(resId, 0L));
                } else {
                    ResourceCountResponseVO resourceCount = new ResourceCountResponseVO();
                    resourceCount.setViewCount(finalViewCountMap.getOrDefault(resId, 0L));
                    resourceCount.setDownloadCount(finalDownloadCountMap.getOrDefault(resId, 0L));
                    resourceBaseResponseVO.setResourceCount(resourceCount);
                }
                queryResultList.add(resourceBaseResponseVO);
            }
        });
        return queryResultList;
    }
}
